//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<task_manager.cpp>
///	@path	~/src/engine/xcore/
///	@date	2007/12/21
///	@desc	Task manager, managed common tasks.

#include "config/config.h"

#include <sstream>

#include "lib/system/thread_debug.h"

#include "engine/xcore/task_manager.h"

TRACE_CATEGORY( _S("Engine/xcore/taskman") , TRACE_LV_TRACE );

namespace xeres {

	// TaskManager
	TaskManager::TaskManager( void )
		: ThreadPool()
		, m_workingTasks( NULL )
		, m_freeTasks( NULL )
		, m_destructing( true )
	{
	}

	// ~TaskManager
	TaskManager::~TaskManager( void )
	{
	}

	// Initialize
	void TaskManager::Initialize( size_t init_task_size , int worker_count , bool lower_priority , const std::vector<bool> * processors )
	{
		// initialize thread pool first
		ThreadPool::Initialize( worker_count , lower_priority , processors );

		if( m_workingTasks || m_freeTasks )
		{
			TRACE_ERROR( _S("TaskManager::Initialize: Task manager has been initialized.") );
		}

		// initialize task pool size
		for( size_t i = 0 ; i < init_task_size ; ++i )
		{
			// create task
			_CommonTask * task = new _CommonTask;
			task->m_next = m_freeTasks;
			m_freeTasks = task;
		}

		m_destructing = false;
	}

	// Finalize
	void TaskManager::Finalize( void )
	{
		m_destructing = true;

		while( m_workingTasks )
		{
			// resolve reset jobs
			// FIXME! may cause dead lock !
			Resolve( Time(0) );
			::Sleep(0);
		}

		// finalize thread pool
		ThreadPool::Finalize();

		XS_ASSERT( m_workingTasks == NULL );

		_CommonTask * task = m_freeTasks;
		while( task )
		{
			_CommonTask * next = task->m_next;
			delete task;
			task = next;
		}

		m_freeTasks = NULL;
	}

	// for task list debugging
#ifdef _DEBUG
	void TaskManager::TaskList( void )
	{
		std::stringstream ss , sx;
		_CommonTask * op = m_workingTasks;
		while( op )
		{
			ss<<" task ";
			ss<<op->m_id;
			ss<<" <" << op->m_status << ">";
			ss<<" -> ";
			op = op->m_next;
		}
		TRACE_ERROR( _S("working : %S") , ss.str().c_str() );
		op = m_freeTasks;
		while( op )
		{
			sx<<" task ";
			sx<<op->m_id;
			sx<<" -> ";
			op = op->m_next;
		}
		TRACE_ERROR( _S("free : %S") , sx.str().c_str() );
	}
#endif

#define TASKLIST()	TaskList();

	// TakeTask
	TaskManager::_CommonTask * TaskManager::TakeTask( _CommonTask * begin , _CommonTask *& last )
	{
		TRACK_FUNCTION_SCOPE();

		_CommonTask * task = begin;
		_CommonTask * next = NULL;
		while( task )
		{
			// secure check
			TaskStatus status = task->m_status;
// 			if( status == TASK_STATUS_IDLE )
// 			{
// 				TRACE_ERROR( _S("Idle task! %i") , task->m_id );
// 			}
			XS_ASSERT( status != TASK_STATUS_IDLE );
			switch( status )
			{
			case TASK_STATUS_WORKING:
				//TRACE_DEBUG( _S("working %i") , task->m_id );
				last = task;
				task = task->m_next;
				continue;
			case TASK_STATUS_COMPLETED:
			case TASK_STATUS_CANCELED:
				{
					//TASKLIST();

					// save next element
					next = task->m_next;

					// detach current
					if( last )
					{
						//TRACE_DEBUG( _S("current %i , last %i , next %i") , task->m_id , last->m_id , next ? next->m_id : -1 );
						assert( last != task );
						last->m_next = next;
					}
					else
					{
						assert( m_workingTasks == task );
						//TRACE_DEBUG( _S("current %i , is header , next %i") , task->m_id , next ? next->m_id : -1 );
						m_workingTasks = next;
						last = next;
						if( next )
							next = m_workingTasks->m_next;
					}

					task->m_next = NULL;
					task->m_completion( TaskResult( status , task->m_return ) );

					// insert to free task list
					task->m_next = m_freeTasks;
					m_freeTasks = task;
					// free task
					task->FreeTask();

					//TASKLIST();
				}
				return next;
			}
		}
		return NULL;
	}

	// Resolve
	void TaskManager::Resolve( Time predict )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main|tick|!_render|!pool*") );

		//TRACE_INFO( _S("Resolve tasks...") );
		//TASKLIST();

		_CommonTask * task = m_workingTasks;
		_CommonTask * last = NULL;
		if( predict == Time(0) )
		{
			// No timing limit.
			while( ( task = TakeTask( task , last ) ) != NULL );
		}
		else
		{
			// setup time guard
			Time start = Time::Now();

			// check & run
			while( Time::Now() - start < predict )
			{
				task = TakeTask( task , last );
				if( task == NULL )
					break;
			}
		}
	}

	// InsertAsyncTask
	void TaskManager::InsertAsyncTask( function<TaskReturn(void)> routine , function<void(TaskResult)> completion )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main|tick|!_render|!pool*") );

		if( m_destructing )
		{
			return;
		}

		if( !ThreadPool::IsInitialized() )
		{
			TRACE_ERROR( _S("TaskManager::InsertAsyncTask: Task manager is not initialized.") );
			completion( TaskResult( TASK_STATUS_CANCELED , TASK_FAILED ) );
			return;
		}
		_CommonTask * task = NULL;
		if( m_freeTasks == NULL )
		{
			// make new
			task = new _CommonTask;
		}
		else
		{
			task = m_freeTasks;
			m_freeTasks = task->m_next;
			//TRACE_ERROR( _S("ERROR: fetch free: %i->%i") , task->m_id , task->m_next ? task->m_next->m_id : -1 );
		}
		task->PrepareNext( routine , completion );
		task->m_next = m_workingTasks;
		m_workingTasks = task;

		ThreadPool::AddJob( task );
	}

	DEBUG_RUN( static int __id = 0 );

	// _CommonTask
	TaskManager::_CommonTask::_CommonTask( void )
		: m_status( TASK_STATUS_IDLE )
		, m_return( TASK_FAILED )
		, m_completion( NULL )
		, m_routine( NULL )
		, m_next( NULL )
		, ThreadPool::Job( true )
	{
		DEBUG_RUN( m_id = __id++ );
	}

	// ~_CommonTask
	TaskManager::_CommonTask::~_CommonTask( void )
	{
		assert( m_status == TASK_STATUS_IDLE || m_status == TASK_STATUS_CANCELED );
	}

	// Execute
	void TaskManager::_CommonTask::Execute( void )
	{
		assert( m_routine != NULL );
		m_return = m_routine();
	}

	// Done
	void TaskManager::_CommonTask::Done( void )
	{
		// try to change status
		TaskStatus status = (TaskStatus)atom_xchg( (volatile int*)&(m_status) , TASK_STATUS_COMPLETED );
		XS_ASSERT( status == TASK_STATUS_WORKING );
		//TRACE_INFO( _S("TaskManager::_CommonTask::Done! %i %i") , m_status , m_id );
	}

	// Canceled
	void TaskManager::_CommonTask::Canceled( void )
	{
		// try to change status
		TaskStatus status = (TaskStatus)atom_xchg( (volatile int*)&(m_status) , TASK_STATUS_CANCELED );
		XS_ASSERT( status == TASK_STATUS_WORKING );
	}

	// PrepareNext
	void TaskManager::_CommonTask::PrepareNext( function<TaskReturn(void)> routine , function<void(TaskResult)> completion )
	{
		assert( m_status == TASK_STATUS_IDLE );
		assert( m_routine == NULL );
		assert( m_completion == NULL );

		m_status = TASK_STATUS_WORKING;
		m_return = TASK_FAILED;
		m_routine = routine;
		m_completion = completion;

		//TRACE_INFO( _S("TaskManager::_CommonTask::PrepareNext %i %i") , m_status , m_id );
	}

	// FreeTask
	void TaskManager::_CommonTask::FreeTask( void )
	{
		m_status = TASK_STATUS_IDLE;
		m_return = TASK_FAILED;
		m_routine = NULL;
		m_completion = NULL;

		//TRACE_INFO( _S("TaskManager::FreeTask %i %i") , m_status , m_id );
	}
	
} // namespace xeres
